{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# **Pandas** Важные методы форматирования данных"
      ],
      "metadata": {
        "id": "y7i7PVp1ZaAR"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "В этом занятии речь пойдет о методах форматирования данных, часто используемых в проектах data science: **merge, sort, reset_index и fillna.** Конечно, есть и другие, поэтому в конце статьи будет шпаргалка с функциями и методами, которые также могут пригодиться."
      ],
      "metadata": {
        "id": "XuDSSRjFZLK1"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Merge в pandas («объединение» Data Frames)"
      ],
      "metadata": {
        "id": "QWx9qBvVZhSh"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "В реальных проектах данные обычно не хранятся в одной таблице. Вместо нее используется много маленьких. И на то есть несколько причин. С помощью нескольких таблиц данными легче управлять, проще избегать «многословия», можно экономить место на диске, а запросы к таблицам обрабатываются быстрее.\n",
        "\n",
        "Суть в том, что при работе с данными довольно часто придется вытаскивать данные из двух и более разных страниц. Это делается с помощью merge.\n",
        "\n",
        "Примечание: хотя в pandas это называется merge, метод почти не отличается от JOIN в SQL.\n",
        "\n",
        "Рассмотрим пример. Для этого можно взять DataFrame zoo, в котором есть разные животные. Но нам понадобится еще один DataFrame — zoo_eats, в котором будет описаны пищевые требования каждого вида."
      ],
      "metadata": {
        "id": "chLIJ-3fbX6d"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "/content/merge-v-pandas.png"
      ],
      "metadata": {
        "id": "i-6DLYz9bwp5"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Теперь нужно объединить два эти Data Frames в один. Чтобы получилось нечто подобное:"
      ],
      "metadata": {
        "id": "oAM3OnnmcAjW"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "/content/obedinenie-dvuh-data-frames-v-odin.png"
      ],
      "metadata": {
        "id": "WhLVAeNScBQb"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "В этой таблице можно проанализировать, например, сколько животных в зоопарке едят мясо или овощи.\n",
        "\n"
      ],
      "metadata": {
        "id": "qBvhuthicXuy"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Как делается merge"
      ],
      "metadata": {
        "id": "vXMXM_7xcdfo"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "В первую очередь нужно создать DataFrame *zoo_eats*, потому что zoo уже имеется из прошлых частей. Для упрощения задачи вот исходные данные:"
      ],
      "metadata": {
        "id": "GbCqqbdojE3F"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "animal;food\n",
        "elephant;vegetables\n",
        "tiger;meat\n",
        "kangaroo;vegetables\n",
        "zebra;vegetables\n",
        "giraffe;vegetables"
      ],
      "metadata": {
        "id": "u1jRK9JCjIyM"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Импортируем библиотеку Pandas"
      ],
      "metadata": {
        "id": "WKAPB3mF08GZ"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import pandas as pd"
      ],
      "metadata": {
        "id": "56ExKZnE0ifL"
      },
      "execution_count": 4,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Прочитаем файл zoo.csv"
      ],
      "metadata": {
        "id": "Cv5c09-X0xlE"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo = pd.read_csv('/content/zoo.csv')"
      ],
      "metadata": {
        "id": "w0Ohf5Jp1BrS"
      },
      "execution_count": 5,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "zoo_eats = pd.DataFrame([['elephant','vegetables'], ['tiger','meat'], ['kangaroo','vegetables'], ['zebra','vegetables'], ['giraffe','vegetables']], columns=['animal', 'food'])"
      ],
      "metadata": {
        "id": "nWy1tqOloF-s"
      },
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "И вот готов DataFrame zoo_eats.\n",
        "\n"
      ],
      "metadata": {
        "id": "vmvoZNMEoLI-"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Теперь пришло время метода merge:"
      ],
      "metadata": {
        "id": "UFjZA_iBoNdt"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats)"
      ],
      "metadata": {
        "id": "sn_zFr9moPTt"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Это было просто, но стоит разобрать, что сейчас произошло:\n",
        "\n",
        "Сначала был указан первый DataFrame (zoo). Потом к нему применен метод .merge(). В качестве его параметра выступает новый DataFrame (zoo_eats). Можно было сделать и наоборот:"
      ],
      "metadata": {
        "id": "VB87V8qToUNl"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo_eats.merge(zoo)"
      ],
      "metadata": {
        "id": "5Q5Rb-XyoYOs"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Это то же самое, что и:"
      ],
      "metadata": {
        "id": "fCPVJ069oZtg"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats)"
      ],
      "metadata": {
        "id": "YcqZ75f3obNm"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Разница будет лишь в порядке колонок в финальной таблице."
      ],
      "metadata": {
        "id": "ymBnV8uQodV3"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Способы объединения: inner, outer, left, right"
      ],
      "metadata": {
        "id": "olyOVUZCofdT"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Базовый метод merge довольно прост. Но иногда к нему нужно добавить несколько параметров.\n",
        "\n",
        "Один из самых важных вопросов — как именно нужно объединять эти таблицы. В SQL есть 4 типа JOIN."
      ],
      "metadata": {
        "id": "qcgj_ZIXojvE"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "/content/tipy-sliyaniya-dataframe.png"
      ],
      "metadata": {
        "id": "vhnC2VphorI4"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "В случае с merge в pandas в теории это работает аналогичным образом.\n",
        "\n",
        "При выборе INNER JOIN (вид по умолчанию в SQL и pandas) объединяются только те значения, которые можно найти в обеих таблицах. В случае же с OUTER JOIN объединяются все значения, даже если некоторые из них есть только в одной таблице.\n",
        "\n",
        "Конкретный пример: в zoo_eats нет значения lion. А в zoo нет значения giraffe. По умолчанию использовался метод INNER, поэтому и львы, и жирафы пропали из таблицы. Но бывают случаи, когда нужно, чтобы все значения оставались в объединенном DataFrame. Этого можно добиться следующим образом:\n",
        "\n"
      ],
      "metadata": {
        "id": "aZxxfXtSotRX"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats, how='outer')"
      ],
      "metadata": {
        "id": "TjUCx6kLovT4"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "/content/obedinenie-dvuh-dataframes-metod-outer.png"
      ],
      "metadata": {
        "id": "njxcI96aoys3"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "В этот раз львы и жирафы вернулись. Но поскольку вторая таблица не предоставила конкретных данных, то вместо значения ставится пропуск (NaN).\n",
        "\n",
        "Логичнее всего было бы оставить в таблице львов, но не жирафов. В таком случае будет три типа еды: vegetables, meat и NaN (что, фактически, значит, «информации нет»). Если же в таблице останутся жирафы, это может запутать, потому что в зоопарке-то этого вида животных все равно нет. Поэтому следует воспользоваться параметром how='left' при объединении.\n",
        "\n",
        "\n",
        "Вот так:"
      ],
      "metadata": {
        "id": "1iSMdYVho7IV"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats, how='left')"
      ],
      "metadata": {
        "id": "fn7Uzagao9XE"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Теперь в таблице есть вся необходимая информация, и ничего лишнего. how = 'left' заберет все значения из левой таблицы (zoo), но из правой (zoo_eats) использует только те значения, которые есть в левой.\n",
        "\n",
        "Еще раз взглянем на типы объединения:"
      ],
      "metadata": {
        "id": "NstpPy3do_Cp"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "/content/tipy-sliyaniya-dataframe_2.png"
      ],
      "metadata": {
        "id": "T93KaIHLo_fj"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Merge в pandas. По какой колонке"
      ],
      "metadata": {
        "id": "k9RoSjwtpJU4"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Для использования merge библиотеке pandas нужны ключевые колонки, на основе которых будет проходить объединение (в случае с примером это колонка animal). Иногда pandas не сможет распознать их автоматически, и тогда нужно указать названия колонок. Для этого нужны параметры left_on и right_on.\n",
        "\n",
        "Например, последний merge мог бы выглядеть следующим образом:"
      ],
      "metadata": {
        "id": "SDiFUYqhpNV-"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats, how = 'left', left_on='animal', right_on='animal')"
      ],
      "metadata": {
        "id": "LVdeLCTapQ4W"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Merge в pandas — довольно сложный метод, но остальные будут намного проще."
      ],
      "metadata": {
        "id": "UHPKdQv_pSxe"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Сортировка в pandas"
      ],
      "metadata": {
        "id": "mJs26FaVpVnS"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Сортировка необходима. Базовый метод сортировки в pandas совсем не сложный. Функция называется sort_values() и работает она следующим образом:\n",
        "\n"
      ],
      "metadata": {
        "id": "wTiVrSzopcbV"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.sort_values('water_need')"
      ],
      "metadata": {
        "id": "Wa22DKftzTh0"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Единственный используемый параметр — название колонки, water_need в этом случае. Довольно часто приходится сортировать на основе нескольких колонок. В таком случае для них нужно использовать ключевое слово **by:**"
      ],
      "metadata": {
        "id": "_P-xoHLAzWrb"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.sort_values(by=['animal', 'water_need'])"
      ],
      "metadata": {
        "id": "kLPt8UzJzZ-r"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "**sort_values** сортирует в порядке возрастания, но это можно поменять на убывание:"
      ],
      "metadata": {
        "id": "IC58vt2IziZY"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.sort_values(by=['water_need'], ascending=False)"
      ],
      "metadata": {
        "id": "D4X67YmqzoZU"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### reset_index()"
      ],
      "metadata": {
        "id": "LUT2JOO0zqyv"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Обратите внимание на беспорядок в нумерации после последней сортировки"
      ],
      "metadata": {
        "id": "F8fJ5ax7zubn"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Это не просто выглядит некрасиво… неправильная индексация может испортить визуализации или повлиять на то, как работают модели машинного обучения.\n",
        "\n",
        "В случае изменения DataFrame нужно переиндексировать строки. Для этого можно использовать метод **reset_index()**. Например:"
      ],
      "metadata": {
        "id": "QvLHta3-z1NU"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.sort_values(by=['water_need'], ascending=False).reset_index()"
      ],
      "metadata": {
        "id": "cGWGS3eaz4pL"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Можно заметить, что новый DataFrame также хранит старые индексы. Если они не нужны, их можно удалить с помощью параметра **drop=True** в функции:"
      ],
      "metadata": {
        "id": "1f9KRFrE0CFO"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.sort_values(by = ['water_need'], ascending = False).reset_index(drop = True)"
      ],
      "metadata": {
        "id": "1V_0FSNk0FtD"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Fillna"
      ],
      "metadata": {
        "id": "6QHL2BPE0Hxo"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "*fillna — это слова fill( заполнить) и na(не доступно).*"
      ],
      "metadata": {
        "id": "7ZiM5GKN0MRe"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Запустим еще раз метод left-merge:"
      ],
      "metadata": {
        "id": "qr5ti9Kr0PD6"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats, how='left')"
      ],
      "metadata": {
        "id": "S3GAapE50Q_p"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Это все животные. Проблема только в том, что для львов есть значение NaN. Само по себе это значение может отвлекать, поэтому лучше заменять его на что-то более осмысленное. Иногда это может быть 0, в других случаях — строка. Но в этот раз обойдемся unknown. Функция **fillna()** автоматически найдет и заменит все значения **NaN** в DataFrame:"
      ],
      "metadata": {
        "id": "-aGVuus10SxN"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "zoo.merge(zoo_eats, how='left').fillna('unknown')"
      ],
      "metadata": {
        "id": "cJhd2FJ00Y-c"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}